#include "mythread.h"

int queue_init(struct queue *qp)
{
	int err;
	qp->q_head = NULL;
	qp->q_tail = NULL;
	qp->job_num = 0;
	qp->job_max = queue_job_max;
	err = pthread_mutex_init(&qp->q_lock, NULL);
	if(err != 0)
		return err;
	return (0);
}

void job_insert(struct queue *qp, struct job *jp)
{
  jp->next = qp->q_head;
	if(qp->q_head == NULL)
		qp->q_tail = jp;
	qp->q_head = jp;
}

void job_remove(struct queue *qp, struct job *jp)
{
	struct job *p = NULL;
  if(jp == qp->q_head)
	{
		qp->q_head = jp->next;
	  if(qp->q_tail == jp)
		{
			qp->q_tail = NULL; qp->q_head = NULL;
		}
	}
  else
		if(jp == qp->q_tail)
		{
			p = qp->q_head;
			while(p->next != qp->q_tail) p = p->next;
			qp->q_tail = p;
		}
	  else
		{
			p = qp->q_head;
			while(p->next != jp) p = p->next;
			p->next = jp->next;
		}
}

void job_count(struct job *jp)
{
	switch(jp->operate)
	{
		case 0:
			jp->result = jp->a + jp->b;
			break;
		case 1:
			jp->result = jp->a - jp->b;
			break;
		case 2:
			jp->result = jp->a * jp->b;
			break;
		case 3:
			if(jp->b == 0) jp->result = 100000;
			else
				jp->result = jp->a / jp->b;
			break;
		default:
			break;
	}

}

int count1 =  0;
void job_read(struct queue *qp)
{
	struct job *jp,*jb;

	for(jp = qp->q_head; jp != NULL; )
	{
	switch(jp->operate)
	{
		case 0:
	  	printf("the calculate of %d + %d is %d\n",jp->a,jp->b,jp->result);
			break;
		case 1:
  		printf("the calculate of %d - %d is %d\n",jp->a,jp->b,jp->result);
			break;
		case 2:
	  	printf("the calcultae of %d * %d is %d\n",jp->a,jp->b,jp->result);
			break;
		case 3:
			if(jp->b == 0) printf("the number 0 is can not be divide\n");
			else
		  printf("the calculate of %d / %d is %d\n",jp->a,jp->b,jp->result);
			break;
		default:
			break;
	}
	count1++;
	jb = jp;
	jp = jp->next;
	free(jb);
	}
}

void *thr_fn1(void *arg)
{
	usleep(1000);
	int i=0;
	int pthr_num =*((int *)arg);
	if(pthr_num < count_thr_num) 
		printf("I am the default calculate thread,my num is ----------------->%d\n",pthr_num);
	else                         
		printf("The caluate is busy,create a new calculate thread,num is ----------------->%d\n",pthr_num);
	while(count > 0)
	{
  	pthread_mutex_lock(&workque[pthr_num].q_lock);
	  if(workque[pthr_num].q_head != NULL)
  	{
   	  pthread_cond_wait(&qready_calculate[pthr_num],&workque[pthr_num].q_lock);
      //calculate the job
	  	job_count(workque[pthr_num].q_tail);
	  	//save the calculate result
		  pthread_mutex_lock(&resultque.q_lock);
	  	count--;
      job_insert(&resultque,workque[pthr_num].q_tail);
		  pthread_mutex_unlock(&resultque.q_lock);
		  workque[pthr_num].job_num--;
      job_remove(&workque[pthr_num],workque[pthr_num].q_tail);
	  }
  	pthread_mutex_unlock(&workque[pthr_num].q_lock);
	}
	thrpool.pool_close = 1;
	pthread_exit(NULL);
}

void *thr_fn2(void *arg)
{
	int i= 0, que_num = 0; 
	for(i = 0 ;i < req_job_num; i++)
	{
	  int flag = 0;
	  pjob  request_job;
	  request_job = (pjob)malloc(sizeof(jobq));
	  request_job->next = NULL;
	  request_job->a = rand()%100;
	  request_job->b = rand()%100;
	  request_job->operate = rand()%4;
	
	  while(!flag)
	  {
		pthread_mutex_lock(&thrpool.t_lock);
	    que_num = rand()%thrpool.cur_count_thr_num;
		pthread_mutex_unlock(&thrpool.t_lock);
	    
	    pthread_mutex_lock(&workque[que_num].q_lock);
			if(workque[que_num].job_num < workque[que_num].job_max)
	 	  {
		    workque[que_num].job_num++;
        job_insert(&workque[que_num], request_job);	
	      pthread_mutex_unlock(&workque[que_num].q_lock);
	      flag = 1;
	    }
      else
  	  {
  		  pthread_cond_signal(&qready_create_thr);
  		  pthread_mutex_unlock(&workque[que_num].q_lock);
  		  usleep(1000);
  	  }
	  }
  
  }
	int j = 0;
	while(count > 0)
	{
	
		for(j = 0; j < thrpool.cur_count_thr_num; j++)
		{
		  pthread_mutex_lock(&workque[j].q_lock);
      if(workque[j].q_head != NULL)
			  pthread_cond_signal(&qready_calculate[j]);
		  pthread_mutex_unlock(&workque[j].q_lock);
		}
	
	}
	pthread_cond_signal(&qready_create_thr);
  pthread_exit(NULL);
}

int threadpool_init()
{
 	int err, res, i =0 ;
 	pthread_t calculate_tid[count_thr_max_num];
	pthread_t request_tid[req_thr_num];
	pthread_attr_t thread_attr;
	res = pthread_attr_init(&thread_attr);
	res = pthread_attr_setdetachstate(&thread_attr, PTHREAD_CREATE_DETACHED);

 	thrpool.cur_count_thr_num = 0;
 	thrpool.pool_close = 0;
 	err = pthread_mutex_init(&thrpool.t_lock, NULL);
	if(err != 0)
		return err;
	
	for(i=0 ; i<count_thr_num; i++)
	{
		pthread_num[i] = i;
		err = pthread_create(&(calculate_tid[i]), &thread_attr, thr_fn1, &pthread_num[i]);
		if(err != 0)
			exit(0);
		++thrpool.cur_count_thr_num;
	}
  thrpool.cur_count_thr_num--;
  
	srand((unsigned)time(NULL));
	for(i=0; i<req_thr_num; i++)
	{
		err = pthread_create(&(request_tid[i]), &thread_attr, thr_fn2, NULL);
		if(err != 0)
			exit(0);
	}
	
	while(!thrpool.pool_close)
	{
		pthread_mutex_lock(&thrpool.t_lock);
		if(thrpool.cur_count_thr_num < count_thr_max_num)
		{
			pthread_cond_wait(&qready_create_thr, &thrpool.t_lock);
			++thrpool.cur_count_thr_num;
			pthread_num[thrpool.cur_count_thr_num] = thrpool.cur_count_thr_num; 
			err = pthread_create(&(calculate_tid[thrpool.cur_count_thr_num ]), &thread_attr, thr_fn1, &pthread_num[thrpool.cur_count_thr_num]);
		  if(err != 0)
			  exit(0);
		}
		pthread_mutex_unlock(&thrpool.t_lock);
		
	}
	(void)pthread_attr_destroy(&thread_attr);
	return 0;
}

int main()
{
	int i = 0;
	int err;
	printf("\nwe have default calculate thread number is %d\n",count_thr_num);
	printf("request thread number is %d ,each request thread create job number is %d\n\n",req_thr_num,req_job_num);
	for(i = 0; i<count_thr_max_num; i++)
	{
  queue_init(&workque[i]);
  pthread_cond_init(&qready_calculate[i],NULL);
	}
  queue_init(&resultque);
	pthread_cond_init(&qready_create_thr,NULL);
	
  threadpool_init();
  while(count!=0);
  for(i=0 ; i<count_thr_max_num; i++)
	{
		pthread_mutex_destroy(&workque[i].q_lock);
		pthread_cond_destroy(&qready_calculate[i]);
	}
  
	job_read(&resultque);
	pthread_mutex_destroy(&resultque.q_lock);
	pthread_mutex_destroy(&thrpool.t_lock);
	printf("The total recive request is %d, and the total calculate is %d\n ",req_thr_num*req_job_num,count1);
  printf("all thread is gone\n");
 
	return 0;
}

